﻿using Microsoft.Playwright;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;

namespace PlaywrightStealth
{
    public static class PlaywrightStealth
    {
        private static string FromFile(string name)
        {
            var assembly = typeof(PlaywrightStealth).Assembly;
            using var stream = assembly.GetManifestResourceStream($"PlaywrightStealth.js.{name}");
            using var reader = new StreamReader(stream);
            return reader.ReadToEnd();
        }

        private static readonly Dictionary<string, string> SCRIPTS = new()
        {
            { "webdrive", FromFile("navigator.webdriver.js") },
            { "navigator_vendor", FromFile("navigator.vendor.js") },
            { "navigator_plugins", FromFile("navigator.plugins.js") },
            { "navigator_permissions", FromFile("navigator.permissions.js") },
            { "navigator_languages", FromFile("navigator.languages.js") },
            { "navigator_platform", FromFile("navigator.platform.js") },
            { "navigator_user_agent", FromFile("navigator.userAgent.js") },
            { "navigator_hardware_concurrency", FromFile("navigator.hardwareConcurrency.js") },
            { "media_codecs", FromFile("media.codecs.js") },
            { "chrome_runtime", FromFile("chrome.runtime.js") },
            { "chrome_loadtimes", FromFile("chrome.loadtimes.js") },
            { "chrome_csi", FromFile("chrome.csi.js") },
            { "chrome_app", FromFile("chrome.app.js") },
            { "iframe_content_window", FromFile("iframe.contentWindow.js") },
            { "outerdimensions", FromFile("window.outerdimensions.js") },
            { "webgl_vendor", FromFile("webgl.vendor.js") },
            { "hairline", FromFile("hairline.js") },
            { "utils", FromFile("utils.js") },
            { "magic-arrays", FromFile("magic-arrays.js") },
        };

        /// <summary>
        /// Playwright Stealth Configuration that applies stealth strategies to Playwright Page objects.
        ///     The stealth strategies are contained in /js package and are basic javascript scripts that are executed
        /// on every Page.goto call.
        /// Note:
        ///     All init scripts are combined by playwright into one script and then executed this means
        ///     the scripts should not have conflicting constants/variables etc. !
        ///     This also means scripts can be extended by overriding enabled_scripts generator:
        ///     ```
        ///     @property
        ///     def enabled_scripts():
        ///         yield 'console.log("first script")'
        ///         yield from super().enabled_scripts()
        ///         yield 'console.log("last script")'
        ///     ```
        /// </summary>
        public class StealthConfig
        {
            // scripts
            public bool webdrive = true;
            public bool webgl_vendor = true;
            public bool navigator_vendor = true;
            public bool navigator_plugins = true;
            public bool navigator_permissions = true;
            public bool navigator_languages = true;
            public bool navigator_platform = true;
            public bool navigator_user_agent = true;
            public int navigator_hardware_concurrency = 4;
            public bool media_codecs = true;
            public bool iframe_content_window = true;
            public bool chrome_runtime = true;
            public bool chrome_loadtimes = true;
            public bool chrome_csi = true;
            public bool chrome_app = true;
            public bool outerdimensions = true;
            public bool hairline = true;

            // options
            public string vendor = "Intel Inc.";
            public string renderer = "Intel Iris OpenGL Engine";
            public string nav_vendor = "Google Inc.";
            public string? nav_user_agent = null;
            public string? nav_platform = null;
            public Tuple<string, string> languages = new("en-US", "en");
            public bool? runOnInsecureOrigins = null;

            public IEnumerable<string> EnabledScripts
            {
                get {
                    var opts = $@"{{
    'webgl_vendor': '{vendor}',
    'webgl_renderer': '{renderer}',
    'navigator_vendor': '{nav_vendor}',
    'navigator_platform': {(nav_platform is null ? "null" : $"'{nav_platform}'")},
    'navigator_user_agent': {(nav_user_agent is null ? "null" : $"'{nav_platform}'")},
    // 'languages': list(self.languages),
    'runOnInsecureOrigins': {(runOnInsecureOrigins.HasValue ? (runOnInsecureOrigins.Value ? "true" : "false") : "null")},
    'hardwareConcurrency': {navigator_hardware_concurrency},
}}";
                    // defined options constant
                    yield return $"const opts = {opts}";
                    yield return $"console.log(opts)";
                    // init utils and magic-arrays helper
                    yield return SCRIPTS["utils"];
                    yield return SCRIPTS["magic-arrays"];
                    // 初始化utils
                    yield return "utils.init();";

                    if (webdrive)
                        yield return SCRIPTS["webdrive"];
                    if (outerdimensions)
                        yield return SCRIPTS["outerdimensions"];
                    if (webgl_vendor)
                        yield return SCRIPTS["webgl_vendor"];
                    if (navigator_vendor)
                        yield return SCRIPTS["navigator_vendor"];
                    if (navigator_plugins)
                        yield return SCRIPTS["navigator_plugins"];
                    if (navigator_permissions)
                        yield return SCRIPTS["navigator_permissions"];
                    if (navigator_languages)
                        yield return SCRIPTS["navigator_languages"];
                    if (navigator_platform)
                        yield return SCRIPTS["navigator_platform"];
                    if (navigator_user_agent)
                        yield return SCRIPTS["navigator_user_agent"];
                    if (media_codecs)
                        yield return SCRIPTS["media_codecs"];
                    if (iframe_content_window)
                        yield return SCRIPTS["iframe_content_window"];
                    if (chrome_runtime)
                        yield return SCRIPTS["chrome_runtime"];
                    if (chrome_loadtimes)
                        yield return SCRIPTS["chrome_loadtimes"];
                    if (chrome_csi)
                        yield return SCRIPTS["chrome_csi"];
                    if (chrome_app)
                        yield return SCRIPTS["chrome_app"];
                    if (hairline)
                        yield return SCRIPTS["hairline"];
                }
            }
        }

        public static async Task StealthAsync(this IBrowserContext browserContext, StealthConfig? config = null)
        {
            foreach (var script in (config ?? new StealthConfig()).EnabledScripts)
                await browserContext.AddInitScriptAsync(script);
        }
        public static async Task StealthAsync(this IPage page, StealthConfig? config = null)
        {
            foreach (var script in (config ?? new StealthConfig()).EnabledScripts)
                await page.AddInitScriptAsync(script);
        }

        
    }
}
